import org.codehaus.groovy.grails.plugins.springsecurity.Secured
import com.st.utils.DataList
import grails.converters.JSON

// TODO: modify to allow user also.
@Secured(['ROLE_ADMIN', 'ROLE_POWER_USER'])
class CompanyController extends AbstractSecurity {

    def personService

    // the delete, save and update actions only accept POST requests
    static allowedMethods = [delete:'POST', save:'POST', update:'POST']

    /** VIEWS **/
    def index = {redirect(action:list, params:params)}

    // List Views
    def list = {
        session.returnController = 'company'
        session.returnAction = 'list'
    }

    def listContacts = {
        def company = Company.get(params.id)
        if(company) {
            println "found the company and are trying to list the contacts for it."
            return [company:company]
        }
        flash.message = "Company not found with id ${params.id}"
        redirect(action:list)
    }

    def listOfficeCalls = {
        redirect controller:'officeCall', action:'list', params:[companyId:params.id]
    }

    def listNotifications = {
        redirect controller:'notification', action:'list', params:[companyId:params.id]
    }

    def listOffices = {
        redirect view:edit, params:[id:params.id]
    }

    def listOfficeHours = {
        redirect view:edit, params:[id:params.id]
    }

    def listOutOfOffice = {
        redirect view:edit, params:[id:params.id]
    }

    /**
     * List the people for the company. Both contactList and repList redirect to 
     * this closure.
     */
    def listPeople = {
        def company = Company.get(params.id)
        return [company:company, type:params.type, title:params.title]
    }

    /**
     * Contact list call. This sets the needed parameters and redirects to listPeople.
     */
    def contactList = {
        redirect(action:listPeople, params:["id":params.id, "type":"Contacts", "title":"Contacts"])
    }

    /**
     * Rep list call. This sets the needed parameters and redirects to listPeople.
     */
    def repList = {
        redirect(action:listPeople, params:["id":params.id, "type":"Reps", "title":"Representatives"])
    }

    // Show Views
    def forwardShow = {
        redirect action:show, id:params.id
    }
    
    def show = {
        def company = Company.get(params.id)
        session.returnController = 'company'
        session.returnAction = 'show'
        if(!company) {
            flash.message = "Company not found with id ${params.id}"
            redirect(action:list)
        } else {
            return [company:company]
        }
    }

    // Create View
    def create = {
        session.removeAttribute("contacts")
        return buildModelForCreate()
    }

    def createContact = {
        redirect controller:'person', action:'create', params:[companyId:params.id, type:'Contacts', returnController:'company', returnAction:params.returnAction]
    }

    def createNotification = {
        redirect controller:'notification', action:'create', params:[companyId:params.id, returnController:'company', returnAction:params.returnAction]
    }

    def createOfficeCall = {
        redirect controller:'officeCall', action:'create', params:[companyId:params.id, returnController:'company', returnAction:params.returnAction]
    }

    // Edit Views
    def edit = {
        def company = Company.get(params.id)
        session.returnController = 'company'
        session.returnAction = 'edit'
        if(!company) {
            flash.message = "Company not found with id ${params.id}"
            redirect(action:list)
        } else {
            return [company:company]
        }
    }

    /** ACTIONS **/
    // Save/Update/Delete Actions
    def addRep = {
//        println "adding reps " + params
        def company = Company.get(params.id)
        if (!company) {
            flash.message = "The company was not found."
            render view:"/company/_peopleList", model:[id:company.id, type:"Reps"]
            return
        }

        def rep = Representative.get(params.person_id)
        if (!rep) {
            flash.message = "The rep to add to the company was not found."
            render view:"/company/_peopleList", model:[id:company.id, type:"Reps"]
            return
        }

        company.addToReps(rep)
        render view:"/company/_peopleList", model:[id:company.id, type:"Reps"]
    }

    def createContactForCompany = {
        def person = createCompanyContact()
        render view:'_createContact', model:[person:person, contacts:session.contacts]
    }

    def delete = {
        def company = Company.get(params.id)
        if(company) {
            try {
                company.delete(flush:true)
                flash.message = "Company ${params.id} deleted"
                redirect(action:list)
            }
            catch(org.springframework.dao.DataIntegrityViolationException e) {
                flash.message = "Company ${params.id} could not be deleted"
                redirect(action:details,id:params.id)
            }
        }
        else {
            flash.message = "Company not found with id ${params.id}"
            redirect(action:list)
        }
    }

    def removePersonFromCompany = {
        def company = Company.get(params.id)
        def person = Person.get(params.personId)

        if (company && person) {
            company.removeFromPeople(person)
        }
        redirect action:editPeople, id:company.id
    }

    def save = {
        boolean errors = false
        def company = new Company(params)
        def rep = Representative.get(params.rep.id)
        if (!rep) {
            company.errors.rejectValue('reps', 'The representative is not valid.')
        } else {
            company.addToReps(rep)
        }
        // person title cannot = "null" this should not be needed because it
        // should convert the title correctly though it is not.
        if (params.person.title == "null") {
            params.person.title = null
        }

        def person = createCompanyContact()
        company.contacts = session.contacts
        if (person) {
            errors = true
        }

        def officeCall = new OfficeCall(params.officeCall)
        if (officeCall.notes || company.contacts) {
            if (!officeCall.hasErrors()) {
                if (company.contacts) {
                    officeCall.contact = company.contacts.toArray()[0]
                }
                officeCall.rep = rep
                if (company.officeCalls) {
                    company.officeCalls.removeAll()
                }
                company.addToOfficeCalls(officeCall)
            } else {
                errors = true
            }
        }
        
        Company.withTransaction {status->
            if (company.validate() && !errors && company.save()) {
                flash.message = "Company ${company} created."
                session.removeAttribute("contacts")
                redirect action:show, id:company.id
            } else {
                status.setRollbackOnly()
                flash.message = "There was a problem creating the company."
                render view:'create', model:buildModelForCreate(company, person, officeCall)
            }
        }
    }

    def update = {
        def company = Company.get(params.id)
        if(company) {
            if(params.version) {
                def version = params.version.toLong()
                if(company.version > version) {
                    company.errors.rejectValue("version", "company.optimistic.locking.failure", "Another user has updated this Company while you were editing.")
                    render(view:'edit',model:[company:company])
                    return
                }
            }
            company.properties = params
            if(!company.hasErrors() && company.save(flush:true)) {
                flash.message = "Company ${params.id} updated"
                render view:'edit', model:[company:company]
            } else {
                render view:'edit', model:[company:company]
            }
        } else {
            flash.message = "Company not found with id ${params.id}"
            redirect(action:list)
        }
    }

    /** JSON DATA **/
    /**
     * Create the list of companies to be used in the default company list table.
     */
    def listAsJSON = {
        // Getting the required values and setting up defaults for the default
        // company list table.
        def sortIndex = params.sidx ?: 'name'
        def sortOrder = params.sord ?: 'asc'
        def maxRows = Integer.valueOf(params.rows)
        def currentPage = Integer.valueOf(params.page) ?: 1
        def rowOffset = currentPage == 1 ? 0 : (currentPage - 1) * maxRows
        def searchValue = params.searchValue
        def companies

        if (!searchValue || searchValue.trim().length() < 1) {
            // Getting all the companies.
            companies = Company.createCriteria().list(max:maxRows, offset:rowOffset) {
                // Set the order.
                if (sortIndex == "phone" || sortIndex == "name") {
                    order(sortIndex, sortOrder).ignoreCase()
                } else {
                    primaryAddress {
                        order(sortIndex, sortOrder).ignoreCase()
                    }
                }
            }
        } else {
            // Getting the companies that satisfy the search criteria.
            searchValue = searchValue.trim()

            // Looking for the state. if the searchValue is a state abbreviation
            // then we get the state and search for only companies in that state.
            def state
            if (searchValue.length() == 2) {
                state = State.findByAbbreviation(searchValue)
            }

            // Search for the company.
            companies = Company.createCriteria().list(max:maxRows, offset:rowOffset) {
                if (state) {
                    // As mentioned above if the searchValue is a state abbreviation
                    // then we search for companies in that state.
                    primaryAddress {
                        eq("state", state)
                    }
                } else {
                    // Searching for the criteria in the fields indicated below.
                    // Using ilike to perform a case insensitive search.
                    or {
                        ilike("name", "%${searchValue}%")
                        ilike("phone", "%${searchValue}%")
                        // Fields from the primary address.
                        primaryAddress {
                            or {
                                ilike("line1", "%${searchValue}%")
                                ilike("city", "%${searchValue}%")
                                ilike("postalCode", "%${searchValue}%")
                            }
                        }
                        // Fields from the contacts.
                        contacts {
                            or {
                                ilike("firstName", "%${searchValue}%")
                                ilike("lastName", "%${searchValue}%")
                                ilike("phone", "%${searchValue}%")
                                ilike("email", "%${searchValue}%")
                                ilike("notes", "%${searchValue}%")
                            }
                        }
                    }
                }

                // Set the order.
                if (sortIndex == "phone" || sortIndex == "name") {
                    order(sortIndex, sortOrder).ignoreCase()
                } else {
                    primaryAddress {
                        order(sortIndex, sortOrder).ignoreCase()
                    }
                }
            }
        }

        // Create the cells string to be converted to json later.
        def jsonCells = companies.collect {
            [cell: [it.name,
                    it.primaryAddress.line1,
                    it.primaryAddress.city,
                    it.primaryAddress.state.abbreviation,
                    it.primaryAddress.postalCode,
                    it.phone,
                    it.primaryAddress.singleLineString()
                ],
                id: it.id]
        }

        // Create the rest of the json string to be used.
        def results = [rows: jsonCells, page:currentPage,
                       records:companies.totalCount,
                       total:Math.ceil(companies.totalCount / maxRows)]

        // If the results are null we set the results to "".
        if (!results) {
            results = ""
        }
        render results as JSON
    }

    def listPeopleForCompanyListAsJSON = {
//        println "listPeopleForCompanyListAsJSON = " + params
        def company = Company.findById(params.id)
        def list
        if (params.type == "Reps") {
            list = company.reps.person
        } else {
            list = company.contacts
        }
//        println "list = " + list
        def jsonCells = list.collect {
            [cell: [it.title?:"",
                    it.firstName,
                    it.lastName,
                    it.phone,
                    it.email,
                    it.notes
                ],
                id: it.id]
        }
//        println jsonCells
        def jsonData = [rows: jsonCells]
//        render DataList.createListWithSize(list.size(), jsonCells) as JSON
        render jsonData as JSON
    }

    def listPeopleNotInCompanyForACAsJSON = {
//        println "listPeopleNotInForCompanyListAsJSON = " + params
        def inList = Company.get(params.id)."${params.type.toLowerCase()}" as List
        def people
        if (params.type == "Reps") {
            def reps = Representative.executeQuery(
                    "SELECT rep\
                    FROM Representative rep, Person person\
                    WHERE concat(person.firstName, ' ', person.lastName) like '${params.query}%'\
                    AND rep.person = person"
            )
//            println "reps = " + reps
            if (inList) {
                reps.removeAll(inList)
            }
            people = reps.collect {
                [id:it.id, name:it.toString()]
            }
        } else {
            def contacts = Person.executeQuery(
                    "SELECT person\
                    FROM Person person\
                    WHERE concat(person.firstName, ' ', person.lastName) like '${params.query}%'"
            )
            if (inList) {
                contacts.removeAll(inList)
            }

            contacts.removeAll(Representative.list().person)
            people = contacts.collect {
                [id:it.id, name:"${it.name}"]
            }
        }
        render DataList.createList(people) as JSON
    }

    def listPhoneForACAsJSON = {
        def list = Company.findAllByPhoneIlike("${params.query}%").phone.unique()
        list = list.collect {
            [id:it, name:it]
        }
        render DataList.createList(list) as JSON
    }

    /** UTILITIES **/
    private Map buildModelForCreate() {
        def model = [:]

        def reps = Representative.list()
        model[('repList')] = reps

        def repId = 1
        def defaultRep = personService.getRepForCurrentUser(Representative.SALES)
        if (defaultRep) {
            repId = defaultRep.id
        }
        model[('repId')] = repId
        return model
    }

    private Map buildModelForCreate(Company company, Person person, OfficeCall officeCall) {
        def model = [:]
        model[('company')] = company

        model[('repList')] = Representative.list()

        def repId = 1
        if (company.reps) {
            repId = (company.reps as List)[0].id
        } else {
            def defaultRep = personService.getRepForCurrentUser(Representative.SALES)
            if (defaultRep) {
                repId = defaultRep.id
            }
        }

        model[('repId')] = repId

        if (person) {
            model[('person')] = person
        }

        if (officeCall) {
            officeCall.company = company
            model[('officeCall')] = officeCall
        }

        return model
    }

    private Person createCompanyContact() {
        def person
        if (params.person?.firstName) {
            person = new Person(params.person)
            if (params.person.title.id == 'null') {
                person.title = null
            }
        }
        if (person && person.validate()) {
            if (!session.contacts) {
                session.contacts = new ArrayList<Person>()
            }
            session.contacts.add(person)
            person = null
        }
        return person
    }
}

/**** Unused code below ****/

//    private Representative getDefaultCompanyRep(List reps, Company company) {
//        def repId = reps[0]
//        if (company.reps) {
//            repId = company.reps[0]
//        } else {
//            def defaultRep = personService.getRepForCurrentUser(Representative.SALES)
//            if (defaultRep) {
//                repId = defaultRep
//            }
//        }
//    }

//    private JSON getPeopleAsJSON(List personList, String link) {
//        def listSize = personList.size()
//        def max = Integer.parseInt(params.max)
//        def list
//        if (listSize > max) {
//            def offset = Integer.parseInt(params.offset)
//            def endRange = (offset + max - 1) <= (listSize - 1) ?
//            (offset + max - 1) : listSize - 1
//            range = offset..endRange
//            list = personList[range]
//        } else {
//            list = personList
//        }
//        list = list.collect {
//            [
//                id:it.id,
//                dataUrl:link + "/$it.person.id",
//                title:it.person.title,
//                name:it.person.name,
//                phone:it.person.phone,
//                email:it.person.email,
//                notes:it.person.notes,
//            ]
//        }
//        DataList.createListWithSize(listSize, list) as JSON
//    }

//    private List getPeopleNotInCompany(Company company) {
//        def allPeople = Person.list()
//        return removePeopleInCompany(company, allPeople)
//    }
//
//    private List removePeopleInCompany(Company company, List people) {
//        def companyPeople = company.people as List
//        def filtered = people.findAll {
//            !(it in companyPeople)
//        }
//        return filtered
//    }

    //////// JQuery Grid Testing Below.
//    def jq_customer_list = {
//        println "getting the jquery grid data for the company."
//        println "params = " + params
//        buildCompanyData(params) as JSON
//    }

//    def jq_customer_contact_list = {
//        def company = Company.findById(params.id)
//        def jsonCells = company.contacts.collect {
//            [cell: [it.title,
//                    it.firstName,
//                    it.lastName,
//                    it.phone,
//                    it.email,
//                    it.notes
//                ],
//                id: it.id]
//        }
//        def jsonData= [rows: jsonCells]
//        render jsonData as JSON
//    }

//    private LinkedHashMap buildJQJSONData(List companies, int currentPage, int maxRows) {
//    }


//        if (!params.max) {
//            params.max = 10
//        }
//
//        def state = State.get(session.searchCompanyState)
//        def results = Company.createCriteria().list(max:params.max, offset:params.offset)  {
//            and {
//                if (session.searchCompanyName) {
//                    ilike("name", "${session.searchCompanyName}%")
//                }
//                if (session.searchDoctorsName) {
//                    ilike("doctorsName", "${session.searchDoctorsName}%")
//                }
//                if (session.searchCompanyPhone) {
//                    ilike("phone", "${session.searchCompanyPhone}%")
//                }
//                primaryAddress {
//                    if (session.searchCompanyCity) {
//                        ilike("city", "${session.searchCompanyCity}%")
//                    }
//                    if (session.searchCompanyState) {
//                        eq("state", state)
//                    }
//                    if (session.searchCompanyPostalCode) {
//                        ilike("postalCode", "${session.searchCompanyPostalCode}%")
//                    }
//                }
//            }
//            if (params.sort == "phone" || params.sort == "email" || params.sort == "name") {
//                order(params.sort, params.order)
//            } else if (params.sort != "name") {
//                primaryAddress {
//                    order(params.sort, params.order)
//                }
//            }
//
//            if (params.sort != "name") {
//                order("name", "asc")
//            }
//        }
//
//        def companies = results.collect {[
//                id:it.id,
//                dataUrl:g.createLink(action:'show') + "/$it.id",
//                companyType:it.type?:'',
//                name:it.name,
//                address:it.primaryAddress.singleLineString(),
//                line1:it.primaryAddress.line1,
//                city:it.primaryAddress.city,
//                state:it.primaryAddress.state.abbreviation,
//                postalCode:it.primaryAddress.postalCode,
//                phone:it.phone
//            ]}
//        render DataList.createListWithSize(results.totalCount, companies) as JSON

//        def people = Company.get(params.id)."${params.type.toLowerCase()}"
//        if (params.type == "Reps") {
//            people = people.person
//        }
//        people = people.collect {[
//                id:it.id,
//                dataUrl:g.createLink(controller:'person', action:'show') + "/$it.id",
//                title:it.title?:"",
//                firstName:it.firstName,
//                lastName:it.lastName?:"",
//                phone:it.phone,
//                email:it.email?:"",
//                notes:it.notes?:""
//            ]}
//
//        people.sort {first, second ->
//            def left
//            def right
//            if (params.order == 'desc') {
//                left = second
//                right = first
//            } else {
//                left = first
//                right = second
//            }
//            left."$params.sort" <=> right."$params.sort"
//        }
//        render DataList.createListWithSize(people.size(), people) as JSON


//    def addContacts = {
//        def company = Company.get(params.id)
//        if (!company) {
//            flash.message = "The company was not found."
//            render view:"/company/_peopleList", model:[id:company.id, type:"Contacts"]
//            return
//        }
//
//        def person = Person.get(params.person_id)
//        if (!person) {
//            flash.message = "The person to add to the company was not found."
//            render view:"/company/_peopleList", model:[id:company.id, type:"Contacts"]
//            return
//        }
//
//        company.addToContacts(person)
//        render view:"/company/_peopleList", model:[id:company.id, type:"Contacts"]
//    }
//

//    private Map buildModelForContacts() {
//        def model = [:]
//        model[('person')] = new Person()
//        return model
//    }

